// Magic Software, Inc.
// http://www.magic-software.com
// Copyright (c) 2000, All Rights Reserved
//
// Source code from Magic Software is supplied under the terms of a license
// agreement and may not be copied or disclosed except in accordance with the
// terms of that agreement.  The various license agreements may be found at
// the Magic Software web site.  This file is subject to the license
//
// RESTRICTED USE SOURCE CODE
// http://www.magic-software.com/License/restricted.pdf

#include "MgcBezierTriangle2.h"
#include "MgcTriMesh.h"

MgcImplementRTTI(MgcBezierTriangle2,MgcBezierTriangle);
MgcImplementStream(MgcBezierTriangle2);

// macros for initialization in the subdivision
#define XA(i) akCtrlPoint[m_auiIndex[i]]
#define CA(i) akCtrlColor[m_auiIndex[i]]
#define TA(i) akCtrlTexture[m_auiIndex[i]]

//----------------------------------------------------------------------------
MgcBezierTriangle2::MgcBezierTriangle2 (unsigned int* auiIndex)
    :
    MgcBezierTriangle(2,6,auiIndex)
{
}
//----------------------------------------------------------------------------
MgcBezierTriangle2::MgcBezierTriangle2 ()
{
}
//----------------------------------------------------------------------------
void MgcBezierTriangle2::InitializePoints (const MgcVector3* akCtrlPoint,
    BlockParameters& rkBP)
{
    // Xuu
    rkBP.m_kXuu = 2.0*(XA(0) - 2.0*XA(1) + XA(2));

    // Xvv
    rkBP.m_kXvv = 2.0*(XA(0) - 2.0*XA(3) + XA(5));

    // Xhh = Xuu - 2*Xuv + Xvv (d/dh = d/ds - d/dt)
    rkBP.m_kXhh = 2.0*(XA(0) + XA(4) - XA(1) - XA(3));
}
//----------------------------------------------------------------------------
void MgcBezierTriangle2::InitializeColors (const MgcColor* akCtrlColor,
    BlockParameters& rkBP)
{
    // Cuu
    rkBP.m_kCuu = 2.0*(CA(0) - 2.0*CA(1) + CA(2));

    // Cvv
    rkBP.m_kCvv = 2.0*(CA(0) - 2.0*CA(3) + CA(5));

    // Chh = Cuu - 2*Cuv + Cvv (d/dh = d/ds - d/dt)
    rkBP.m_kChh = 2.0*(CA(0) + CA(4) - CA(1) - CA(3));
}
//----------------------------------------------------------------------------
void MgcBezierTriangle2::InitializeTextures (const MgcVector2* akCtrlTexture,
    BlockParameters& rkBP)
{
    // Tuu
    rkBP.m_kTuu = 2.0*(TA(0) - 2.0*TA(1) + TA(2));

    // Tvv
    rkBP.m_kTvv = 2.0*(TA(0) - 2.0*TA(3) + TA(5));

    // Thh = Tuu - 2*Tuv + Tvv (d/dh = d/ds - d/dt)
    rkBP.m_kThh = 2.0*(TA(0) + TA(4) - TA(1) - TA(3));
}
//----------------------------------------------------------------------------
void MgcBezierTriangle2::Tessellate (unsigned int uiLevel,
    const MgcVector3* akCtrlPoint, const MgcColor* akCtrlColor,
    const MgcVector2* akCtrlTexture, MgcTriMesh* pkMesh,
    unsigned int& ruiVertexStart, unsigned int& ruiTriangleStart)
{
    GenerateConnectivity(uiLevel,pkMesh,ruiTriangleStart);

    // number of vertices in the subdivision
    unsigned int uiQuantity = GetVerticesPerTrianglePatch(uiLevel);

    // indices of three corners of patch, I0 (w=1), I1 (u=1), I2 (v=1)
    unsigned int uiTwoPowL = (1 << uiLevel);
    unsigned int uiI0 = 0;
    unsigned int uiI1 = uiTwoPowL;
    unsigned int uiI2 = uiTwoPowL*(uiTwoPowL + 3) >> 1;

    BlockParameters kBP;

    // vertices for subdivision
    MgcVector3* akX = pkMesh->Vertices() + ruiVertexStart;
    akX[uiI0] = XA(0);
    akX[uiI1] = XA(2);
    akX[uiI2] = XA(5);

    // derivatives for subdivision (for normal vectors)
    MgcVector3* akXu;
    MgcVector3* akXv;
    if ( pkMesh->Normals() )
    {
        akXu = new MgcVector3[uiQuantity];
        akXu[uiI0] = 2.0*(XA(1) - XA(0));
        akXu[uiI1] = 2.0*(XA(2) - XA(1));
        akXu[uiI2] = 2.0*(XA(4) - XA(3));

        akXv = new MgcVector3[uiQuantity];
        akXv[uiI0] = 2.0*(XA(3) - XA(0));
        akXv[uiI1] = 2.0*(XA(5) - XA(3));
        akXv[uiI2] = 2.0*(XA(4) - XA(1));
    }
    else
    {
        akXu = 0;
        akXv = 0;
    }

    // colors for subdivision
    MgcColor* akColor;
    if ( pkMesh->Colors() )
    {
        akColor = pkMesh->Colors() + ruiVertexStart;
        akColor[uiI0] = CA(0);
        akColor[uiI1] = CA(2);
        akColor[uiI2] = CA(5);
    }
    else
    {
        akColor = 0;
    }

    // textures for subdivision
    MgcVector2* akTexture;
    if ( pkMesh->Textures() )
    {
        akTexture = pkMesh->Textures() + ruiVertexStart;
        akTexture[uiI0] = TA(0);
        akTexture[uiI1] = TA(2);
        akTexture[uiI2] = TA(5);
    }
    else
    {
        akTexture = 0;
    }

    // recursive subdivision
    if ( uiLevel > 0 )
    {
        InitializePoints(akCtrlPoint,kBP);

        if ( akCtrlColor )
            InitializeColors(akCtrlColor,kBP);

        if ( akCtrlTexture )
            InitializeTextures(akCtrlTexture,kBP);

        SubdivideLL(--uiLevel,0.25,akX,akXu,akXv,akColor,akTexture,uiI0,uiI1,
            uiI2,kBP);
    }

    // calculate unit-length normals from derivative vectors
    if ( pkMesh->Normals() )
    {
        MgcVector3* akNormal = pkMesh->Normals() + ruiVertexStart;
        for (unsigned int uiI = 0; uiI < uiQuantity; uiI++)
            akNormal[uiI] = akXu[uiI].UnitCross(akXv[uiI]);
        delete[] akXu;
        delete[] akXv;
    }

    ruiVertexStart += uiQuantity;
}
//----------------------------------------------------------------------------
void MgcBezierTriangle2::SubdivideLL (unsigned int uiLevel, MgcReal fDSqr,
    MgcVector3* akX, MgcVector3* akXu, MgcVector3* akXv, MgcColor* akColor,
    MgcVector2* akTexture, unsigned int uiI0, unsigned int uiI1,
    unsigned int uiI2, BlockParameters& rkBP)
{
    /*
     i2
     +
     | \
     |   \
     +----+
     i0    i1
    */

    // subdivision indices
    unsigned int uiI01 = (uiI0 + uiI1) >> 1;
    unsigned int uiD10 = uiI1 - uiI0;
    unsigned int uiI02 = (((uiI0 + uiI2) << 2) + uiD10*uiD10) >> 3;
    unsigned int uiI12 = uiI02 + (uiD10 >> 1);

    // vertices

    // bottom u-edge subdivision
    akX[uiI01] = 0.5*(akX[uiI0] + akX[uiI1] - fDSqr*rkBP.m_kXuu);

    // left v-edge subdivision
    akX[uiI02] = 0.5*(akX[uiI0] + akX[uiI2] - fDSqr*rkBP.m_kXvv);

    // hypotenuse edge subdivision
    akX[uiI12] = 0.5*(akX[uiI1] + akX[uiI2] - fDSqr*rkBP.m_kXhh);

    // derivatives (for normal vectors)
    if ( akXu )
    {
        // bottom u-edge subdivision
        akXu[uiI01] = 0.5*(akXu[uiI0] + akXu[uiI1]);
        akXv[uiI01] = 0.5*(akXv[uiI0] + akXv[uiI1]);

        // left v-edge subdivision
        akXu[uiI02] = 0.5*(akXu[uiI0] + akXu[uiI2]);
        akXv[uiI02] = 0.5*(akXv[uiI0] + akXv[uiI2]);

        // hypotenuse edge subdivision
        akXu[uiI12] = 0.5*(akXu[uiI1] + akXu[uiI2]);
        akXv[uiI12] = 0.5*(akXv[uiI1] + akXv[uiI2]);
    }

    // colors
    if ( akColor )
    {
        // bottom u-edge subdivision
        akColor[uiI01] = 0.5*(akColor[uiI0] + akColor[uiI1] -
            fDSqr*rkBP.m_kCuu);

        // left v-edge subdivision
        akColor[uiI02] = 0.5*(akColor[uiI0] + akColor[uiI2] -
            fDSqr*rkBP.m_kCvv);

        // hypotenuse edge subdivision
        akColor[uiI12] = 0.5*(akColor[uiI1] + akColor[uiI2] -
            fDSqr*rkBP.m_kChh);
    }

    // textures
    if ( akTexture )
    {
        // bottom u-edge subdivision
        akTexture[uiI01] = 0.5*(akTexture[uiI0]+akTexture[uiI1]
            - fDSqr*rkBP.m_kTuu);

        // left v-edge subdivision
        akTexture[uiI02] = 0.5*(akTexture[uiI0]+akTexture[uiI2]
            - fDSqr*rkBP.m_kTvv);

        // hypotenuse edge subdivision
        akTexture[uiI12] = 0.5*(akTexture[uiI1]+akTexture[uiI2]
            - fDSqr*rkBP.m_kThh);
    }

    // recurse on four children
    if ( uiLevel > 0 )
    {
        uiLevel--;
        fDSqr *= 0.25;

        // subtriangle <(s0,t0),(sm,t0),(s0,tm)>
        SubdivideLL(uiLevel,fDSqr,akX,akXu,akXv,akColor,akTexture,uiI0,uiI01,
            uiI02,rkBP);

        // subtriangle <(sm,t0),(s1,t0),(sm,tm)>
        SubdivideLL(uiLevel,fDSqr,akX,akXu,akXv,akColor,akTexture,uiI01,uiI1,
            uiI12,rkBP);

        // subtriangle <(s0,tm),(sm,tm),(s0,t1)>
        SubdivideLL(uiLevel,fDSqr,akX,akXu,akXv,akColor,akTexture,uiI02,uiI12,
            uiI2,rkBP);

        // subtriangle <(sm,t0),(sm,tm),(s0,tm)>
        SubdivideUR(uiLevel,fDSqr,akX,akXu,akXv,akColor,akTexture,uiI01,uiI12,
            uiI02,rkBP);
    }
}
//----------------------------------------------------------------------------
void MgcBezierTriangle2::SubdivideUR (unsigned int uiLevel, MgcReal fDSqr,
    MgcVector3* akX, MgcVector3* akXu, MgcVector3* akXv, MgcColor* akColor,
    MgcVector2* akTexture, unsigned int uiI0, unsigned int uiI1,
    unsigned int uiI2, BlockParameters& rkBP)
{
    /*
     i2   i1
     +----+
       \  |
         \|
          +
          i0
    */

    // subdivision indices
    unsigned int uiI12 = (uiI1 + uiI2) >> 1;
    unsigned int uiD12 = uiI1 - uiI2;
    unsigned int uiI01 = (((uiI0 + uiI1) << 2) + uiD12*uiD12) >> 3;
    unsigned int uiI02 = uiI01 - (uiD12 >> 1);

    // vertices

    // top u-edge subdivision
    akX[uiI12] = 0.5*(akX[uiI1] + akX[uiI2] - fDSqr*rkBP.m_kXuu);

    // right v-edge subdivision
    akX[uiI01] = 0.5*(akX[uiI0] + akX[uiI1] - fDSqr*rkBP.m_kXvv);

    // hypotenuse edge subdivision
    akX[uiI02] = 0.5*(akX[uiI0] + akX[uiI2] - fDSqr*rkBP.m_kXhh);

    // colors
    MgcColor kCuu12, kCvv12, kChh12, kCuu01, kCvv01, kChh01, kCuu02, kCvv02,
        kChh02;
    if ( akColor )
    {
        // top u-edge subdivision
        akColor[uiI12] = 0.5*(akColor[uiI1] + akColor[uiI2] -
            fDSqr*rkBP.m_kCuu);

        // right v-edge subdivision
        akColor[uiI01] = 0.5*(akColor[uiI0] + akColor[uiI1] -
            fDSqr*rkBP.m_kCvv);

        // hypotenuse edge subdivision
        akColor[uiI02] = 0.5*(akColor[uiI0] + akColor[uiI2] -
            fDSqr*rkBP.m_kChh);
    }

    // textures
    if ( akTexture )
    {
        // top u-edge subdivision
        akTexture[uiI12] = 0.5*(akTexture[uiI1]+akTexture[uiI2]
            - fDSqr*rkBP.m_kTuu);

        // right v-edge subdivision
        akTexture[uiI01] = 0.5*(akTexture[uiI0]+akTexture[uiI1]
            - fDSqr*rkBP.m_kTvv);

        // hypotenuse edge subdivision
        akTexture[uiI02] = 0.5*(akTexture[uiI0]+akTexture[uiI2]
            - fDSqr*rkBP.m_kThh);
    }

    // recurse on four children
    if ( uiLevel > 0 )
    {
        uiLevel--;
        fDSqr *= 0.25;

        // subtriangle <(sm,tm),(sm,t1),(s0,t1)>
        SubdivideUR(uiLevel,fDSqr,akX,akXu,akXv,akColor,akTexture,uiI02,uiI12,
            uiI2,rkBP);

        // subtriangle <(s1,tm),(s1,t1),(sm,t1)>
        SubdivideUR(uiLevel,fDSqr,akX,akXu,akXv,akColor,akTexture,uiI01,uiI1,
            uiI12,rkBP);

        // subtriangle <(s1,t0),(s1,tm),(sm,tm)>
        SubdivideUR(uiLevel,fDSqr,akX,akXu,akXv,akColor,akTexture,uiI0,uiI01,
            uiI02,rkBP);

        // subtriangle <(sm,tm),(s1,tm),(sm,t1)>
        SubdivideLL(uiLevel,fDSqr,akX,akXu,akXv,akColor,akTexture,uiI02,uiI01,
            uiI12,rkBP);
    }
}
//----------------------------------------------------------------------------

//---------------------------------------------------------------------------
// streaming
//---------------------------------------------------------------------------
MgcObject* MgcBezierTriangle2::Factory (MgcStream& rkStream)
{
    MgcBezierTriangle2* pkObject = new MgcBezierTriangle2;
    MgcStream::Link* pkLink = new MgcStream::Link(pkObject);
    pkObject->Load(rkStream,pkLink);
    return pkObject;
}
//---------------------------------------------------------------------------
void MgcBezierTriangle2::Load (MgcStream& rkStream, MgcStream::Link* pkLink)
{
    MgcBezierTriangle::Load(rkStream,pkLink);
}
//---------------------------------------------------------------------------
void MgcBezierTriangle2::Link (MgcStream& rkStream, MgcStream::Link* pkLink)
{
    MgcBezierTriangle::Link(rkStream,pkLink);
}
//---------------------------------------------------------------------------
bool MgcBezierTriangle2::Register (MgcStream& rkStream)
{
    return MgcBezierTriangle::Register(rkStream);
}
//---------------------------------------------------------------------------
void MgcBezierTriangle2::Save (MgcStream& rkStream)
{
    MgcBezierTriangle::Save(rkStream);
}
//---------------------------------------------------------------------------
